package IC;

import IC.AST.ICClass;
import IC.AST.PrettyPrinter;
import IC.AST.Program;
import IC.Context.BuildTypeTableVisitor;
import IC.Context.ContextAnalysisVisitor;
import IC.Context.ContextWarning;
import IC.Context.InitializationTestVisitor;
import IC.Symbols.SymbolTable;
import IC.Types.TypeTable;
import IC.Parser.Lexer;
import IC.Parser.LibraryParser;
import IC.Parser.Parser;
import IC.Parser.SyntaxError;
import java_cup.runtime.Symbol;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.List;

public class Compiler {
    public static void main(String[] args) {
    	if (args.length == 0) {
    		System.out.println("Invalid arguments supplied.");
    		printUsage();
    		return;
    	}
    	
    	String icFilePath = null;
    	String libraryFilePath = "libic.sig";
    	boolean printAST = false;
        boolean printSym = false;
    	
    	for (int argIndex = 0; argIndex < args.length; argIndex++) {
    		if (args[argIndex].startsWith("-L")) {
    			libraryFilePath = args[argIndex].substring(2);
    		} else if ("-print-ast".equalsIgnoreCase(args[argIndex])) {
    			printAST = true;
    		} else if ("-dump-symtab".equalsIgnoreCase(args[argIndex])) {
                printSym = true;
            } else if (!args[argIndex].startsWith("-")) {
                if (icFilePath != null) {
                    System.out.println("Invalid arguments supplied.");
                    printUsage();
                    return;
                }
    			icFilePath = args[argIndex];
    		} else {
    			System.out.println("Invalid arguments supplied.");
	    		printUsage();
	    		return;
    		}
    	}
    	
    	if (icFilePath == null) {
    		System.out.println("No ic file was supplied.");
    		printUsage();
    		return;
    	}
        
        String icFileName = (new File(icFilePath)).getName();
    	
    	FileReader fr = null;
    	try {
    		 fr = new FileReader(icFilePath);
    	} catch (FileNotFoundException e) {
    		System.out.println("Could not open input file: " + icFilePath);
    		return;
    	}
    	
    	ICClass libraryClass = null;
        FileReader lfr = null;
        try {
             lfr = new FileReader(libraryFilePath);
        } catch (FileNotFoundException e) {
            System.out.println("Could not open library file: " + libraryFilePath);
            return;
        }

        LibraryParser libParser = new LibraryParser(new Lexer(lfr));

        try {
            Symbol res = libParser.parse();
            if (libParser.getSyntaxErrors().size() > 0) {
                printSyntaxErrors(libParser.getSyntaxErrors());
                return;
            }

            libraryClass = (ICClass)res.value;
        } catch (Exception e) {
            System.out.println(e);
            return;
        }

    	
    	Parser parser = new Parser(new Lexer(fr));
    	Program program = null; 
    	try {
    		Symbol res = parser.parse();
    		if (parser.getSyntaxErrors().size() > 0) {
    			printSyntaxErrors(parser.getSyntaxErrors());
    			return;
    		} 
    		
    		program = (Program)res.value;
    		
    	} catch (Exception e) {
    		System.out.println(e);
    		return;
    	}

        if (libraryClass != null) {
            //adding at position 0 for consistency with supplied output.
            program.getClasses().add(0, libraryClass);
        }

        BuildTypeTableVisitor typeTableVisitor = new BuildTypeTableVisitor(icFileName);
        program.accept(typeTableVisitor);
        if (typeTableVisitor.getError() != null) {
            System.out.println(typeTableVisitor.getError());
            return;
        }

        TypeTable typeTable = typeTableVisitor.getTable();

        ContextAnalysisVisitor contextVisitor = new ContextAnalysisVisitor(typeTable, icFileName);
        program.accept(contextVisitor);
        if (contextVisitor.getError() != null) {
            System.out.println(contextVisitor.getError());
            return;
        }

        SymbolTable symTable = contextVisitor.getGlobalSymTable();

        InitializationTestVisitor itv = new InitializationTestVisitor(typeTable);
        program.accept(itv);
        for (ContextWarning cw : itv.getWarnings()) {
            System.out.println(cw);
        }
       
    	if (printAST) {
    		PrettyPrinter programPP = new PrettyPrinter(icFileName);
    		System.out.println(program.accept(programPP));
    	}
        
        if (printSym) {
            System.out.println(symTable.toString());
            System.out.println(typeTable.toString());
        }
    }
    
    private static void printUsage() {
    	System.out.println("Usage: java IC.Compiler <file.ic> [-L<path to libic.sig>] [-print-ast] [-dump-symtab]");
    }
    
    private static void printSyntaxErrors(List<SyntaxError> errors) {
    	for (SyntaxError error : errors) {
    		System.out.println(error);
    	}
    }
    
}
